# -*- coding: UTF-8 -*-

import Pyro4
import socket
import sqlite3
import sys
import random
import action
from tkinter import *

sys.path.append("../errors")

import OrionErrors

class BaseDeDonnees():
    def __init__(self):
        self.creerTables() #Fonction pour creer les tables au depart si elles n'existent pas
        self.listeJoueurs = []
        self.listeCouleursJoueurs = ["blue","red","chocolate","orange","OliveDrab","pink","peru","yellow"]
        self.listeChat = []
        self.listeFrames = []
        self.createurPartie = "rien"
        self.creerTables()
    
    def ouvrirConnexions(self):
        self.conn = sqlite3.connect('ProjetOrion.sqlite') #Connexion a la Base de Donnees SQLite
        self.c = self.conn.cursor() #Curseur a utiliser sur la connexion
        
    def creerTables(self):
        self.ouvrirConnexions()
        #Cree la table "Utilisateurs" avec les colonnes "Utilisateur" et "Mot_De_Passe" avec comme PK "Utilisateur"
        self.c.execute("""CREATE TABLE IF NOT EXISTS Utilisateurs(Utilisateur TEXT, Mot_De_Passe TEXT, CONSTRAINT pk_NomUtilisateur PRIMARY KEY(Utilisateur))""")
        self.fermerConnexions()
        
    def chercherDoublonJoueur(self, nouveau):
        for joueur in self.listeJoueurs:
            print(joueur)
            if joueur == nouveau:
                print("non")
                raise OrionErrors.DoubleConnexionError(nouveau) #Envoie une exception si le joueur est deja connecte
                                  
        return False
        
    def validerLogin(self, nomUtilisateur, motDePasse):
        self.ouvrirConnexions()
        self.loginValide = False
        
        try:
            #Verifie si le nom d'utilisateur specifie et le mot de passe correspondent
            if self.chercherDoublonJoueur(nomUtilisateur) == False:
                #"SELECT 1" est plus efficace que de fetcher des valeurs sous format texte
                self.c.execute("""SELECT 1 FROM Utilisateurs WHERE Utilisateur = ? AND Mot_De_Passe = ?""", (nomUtilisateur, motDePasse))
                if self.c.fetchone(): #Si la requete a donne un resultat, c'est que le login est bon
                    self.loginValide = True
                    #ajoute le joueur a la liste
                    self.ajouterListeJoueurs(nomUtilisateur)
                else:
                    self.loginValide = False
        finally:
            self.fermerConnexions()
        
        return self.loginValide
        
    def creerNouveauCompte(self, nomUtilisateur, motDePasse):
        try: #Bloc try afin d'attraper l'exception si l'usager existe deja. Pas de bloc catch, car l'exception doit apparaitre du cote du client
            self.ouvrirConnexions()
            self.c.execute("""INSERT INTO Utilisateurs (Utilisateur, Mot_De_Passe) values (?,?)""", (nomUtilisateur, motDePasse))
            self.conn.commit()
        finally: #La connexion et le curseur doivent absoluement etre fermes meme s'il y a une exception
            self.fermerConnexions()
        
    def fermerConnexions(self): #Important de fermer la connexion et le curseur afin qu'il n'y ait pas de "memory leaks"
        self.c.close()
        self.conn.close()
        
    def ajouterListeJoueurs(self, nom):
        self.listeJoueurs.append(nom)
        self.listeFrames.append(0)
        
        if self.listeJoueurs[0] == nom:
            self.createurPartie = nom
        
    def retournerListeJoueursActifs(self):
        return self.listeJoueurs
    
    def retournerListeCouleurs(self):
        return self.listeCouleursJoueurs
    
    def modifierListeCouleurs(self):
        pass
    
    def retirerDeLaListe(self, joueur):
        for joueurL in self.listeJoueurs:
            if joueurL == joueur:
                del(self.listeFrames[self.listeJoueurs.index(joueur)])
                self.listeJoueurs.remove(joueur)
                
    def insererTexteChat(self, nom, texte):
        if texte != "":
            self.listeChat.append(nom + " dit : " + texte)

    def creeListeChat(self):
        return self.listeChat
        
class Controleur():
    def __init__(self, ip):
        self.baseDeDonnees = BaseDeDonnees()
        self.ip = ip
        self.actionsLog = []
        self.partieDemarree = 0
        self.freezeAtFrame = 0
        self.seed = random.random()*50000
        self.id = 0
        
    def validerLogin(self, nomUtilisateur, motDePasse):
        loginValide = self.baseDeDonnees.validerLogin(nomUtilisateur, motDePasse) #Appel de la methode de validation du login dans la base de donnees 
        
        return loginValide 
        
    def creerNouveauCompte(self, nomUtilisateur, motDePasse):
        self.baseDeDonnees.creerNouveauCompte(nomUtilisateur, motDePasse)
        
    def receptionFrame(self, nomJoueur, frame, actionsRecues):
        self.baseDeDonnees.listeFrames[self.baseDeDonnees.listeJoueurs.index(nomJoueur)] = frame
           
        debutActions = self.getMaxFrame() + 2 #Appose aux actions a quel frame ils doivent debuter pour que ce soit synchronise pour tout le monde (frame le plus avance + 2)
         
        for action in actionsRecues:
            action.frame = debutActions
            self.actionsLog.append(action)
                              
        actionsAEffectuer = []
        
        self.synchronisationFrames(nomJoueur, frame)
        
        for action in self.actionsLog:
            if action.frame == frame:
                actionsAEffectuer.append(action)
                
        return actionsAEffectuer
    
    def synchronisationFrames(self, nomJoueur, frameJoueur):
        maxFrame = self.getMaxFrame()
        minFrame = self.getMinFrame()
        
        if maxFrame >= minFrame + 4: #S'il y a 200ms d'ecart entre le joueur et le joueur le plus en retard
            self.freezeAtFrame = maxFrame #On freeze tous les joueurs lorsqu'ils atteignent le frame du joueur le plus eleve
        elif maxFrame == minFrame:
            self.freezeAtFrame = 0                
    
    def getGeneralFreeze(self):
        return self.freezeAtFrame
    
    def retournerListeJoueurs(self):
        return self.baseDeDonnees.retournerListeJoueursActifs()
    
    def retirerDeLaListe(self, joueur):
        self.baseDeDonnees.retirerDeLaListe(joueur)
    
    def insererTexteChat(self, nom, texte):
        self.baseDeDonnees.insererTexteChat(nom, texte)
    
    def creeListeChat(self):
        return self.baseDeDonnees.creeListeChat()
    
    def retournerListeCouleurs(self):
        return self.baseDeDonnees.retournerListeCouleurs()
  
    def getIp(self):
        return self.ip
    
    def getCreateurPartie(self):
        return self.baseDeDonnees.createurPartie
    
    def debuterPartie(self):
        self.partieDemarree = 1
    
    def getSeed(self):
        return self.seed
    
    def isPartieDemarree(self):
        return self.partieDemarree
    
    def getMinFrame(self):
        minFrame = -1
    
        #Boucle pour rechercher le plus petit frame dans la liste des frames
        for frame in self.baseDeDonnees.listeFrames:
            if minFrame == -1:
                minFrame = frame
            elif minFrame > frame:
                minFrame = frame
        
        return minFrame
        
    def getMaxFrame(self):
        maxFrame = 0
        
        for frame in self.baseDeDonnees.listeFrames:
            if maxFrame < frame:
                maxFrame = frame
                
        return maxFrame
    
    #methode pr generer un id        
    def genererId(self):
        self.id = self.id+1
        return self.id
        
if __name__ == "__main__":
    ip = socket.gethostbyname(socket.gethostname())
    daemon = Pyro4.core.Daemon(host=ip, port=54440) #Le processus qui ecoute les messages des clients 
    daemon.register(Controleur(ip), "serveur") #Un objet Controleur() dont les methodes peuvent etre invoquees, connu sous le nom de "serveur"

    print("Serveur Pyro actif sous le nom \'serveur\'") #Juste pour voir quelque chose sur la console du serveur pour etre sur qu'il est demarre  
    daemon.requestLoop() #On demarre l'ecoute des requetes
    
